跳到主要内容

webpack

Create by fall on 07 Dec 2020 Recently revised in 14 Mar 2024

webpack

webpack 是一个前端项目构建工具,为 web 开发中的面临的问题——更好的构建大型 SPA 提供方案。

webpack 可以:代码打包;代码分割;代码预处理

https://mp.weixin.qq.com/s/SbJNbSVzSPSKBe2YStn2Zw webpack 的万字总结,如果项目确实用到,可以再看一遍

五个基本概念

  • 入口(entry):就是构建开始的地方,可以是多个
  • 出口(output):就是构建好后,内容存放的地方
  • loader:配置后可以处理除去 .js 之外的文件,.less.sass.vue
  • 插件(plugins):从打包优化和压缩,到重新定义环境中的变量。功能极其强大。
  • 模式(mode):不同的模式配置,会对应不同的场景(development,production)

在使用 webpack 构建的应用程序或站点中,有三类代码:

  • 个人或者团队编写的代码。
  • 这些代码依赖的任何第三方的 library 或者 "vendor" 代码(node_modules)。
  • webpack 的 runtime 和 manifest 管理所有模块的交互
  • runtime:在模块交互时,连接模块所需的加载和解析逻辑;包括浏览器中的已加载模块的连接,以及懒加载模块的执行逻辑。
  • manifest:当编译器(compiler)开始执行、解析、映射应用程序时,它会保留所有模块的详细要点,这个数据集合称为 "Manifest"。当完成打包并发送到浏览器时,会在运行时通过 manifest 来解析、加载模块。
  • runtime 和 manifest 之间的交互:浏览器在运行时,runtime 和 manifest 用来连接应用模块化的所有代码。无论是使用哪一种模块语法,那些 import 或者 require 语句都已经转化为 __webpack_require__ 方法,此方法指向模块标识符(module identifier),在使用 manifest 中的数据时,runtime 能够查询模块标识符,检索出背后对应的模块。

使用

  • 搭建 webpack 环境 npm install webpack webpack-cli -D

  • 使用 npm init -y 初始化环境

  • 新建 src 作为源代码库

  • src 中创建主页 index.html

  • 初始化首页基本的结构

  • npm install jQuery -S 安装 jQuery

  • 进行开发

webpack的设置

webpack 的默认入口为:src 中的 index.js

webpack 的默认出口文件是:dist 中的 main.js

// 最简单的 webpack 配置
module.export = {
mode:'development' // 设置使用开发模式,也可以改为production:生产模式
}

两种模式的区别

// development :开发者模式(开发环境),会将 process.env.NODE_ENV 的值设为 development。启用  NamedChunksPlugin 和 NamedModulesPlugin
// production :生产环境,会将 process.env.NODE_ENV 的值设为 production。 启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin
if(process.env.NODE_ENV === 'development'){
//开发环境 do something
}else{
//生产环境 do something
}

为 webpack 设置入口和出口

const path =require('path') // 导入node 自带模块,path
module.exports = {
entry:path.join(__dirname,'./src/index.js'), // entry 接受字符串
// 告诉 webpack 将从那个文件开始构建,这个文件将作为 webpack 关系依赖图的起点
output:{
path:path.join(__dirname,"./dist"),// 输出文件存放路径
filename:'bundle.js' // 输出文件的名称
}
}
// 其中 entry 也可以接受对象,进而进行多个网页的配置
// {main: './src/main.js'}

配置多个入口

// 应用场景1
// 分离 应用程序(app) 和 第三方库(vendor) 的 webpack 配置
module.exports ={
entry:{
app:'./src/app.js',
vendors:'./srcvendors.js'
}
}
// 应用场景2
// 告诉 webpack 需要三个独立分离的依赖图
module.exports = {
entry:{
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
}

配置多个出口

const path = require('path');
module.exports = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}

脚本自动打包

安装插件

npm install webpack-dev-server -D 该 webpack 插件,可以实现监听 webpack 文件,并且进行自动打包

// package.json
"scripts":{
"dev":"webpack-dev-server", // 不再是通过 webpack 进行执行
"build":"webpack -p" // 此时会读取相关的打包配置,按照给定的配置,将项目生成到 dist 中
}
// 也可以更改 webpack 后面的参数,webpack-dev-server --open --host 127.0.0.1 --port 8888
// 这些参数的意义是,自动打开浏览器,并且 在127.0.0.1 端口号为 8888 上创建新的项目

webpack 的注意点

webpack-dev-server 会启动一个实时打包的 http 服务器

webpack-dev-server 打包生成的文件默认存放到根目录当中,并且是虚拟的,不可见的

然后就可以通过 npm run dev 来编译代码了

其他的配置

npm run build 通常用来重新编译项目,观察者模式会监听文件的修改,但是不会触发浏览器的刷新,需要手动进行刷新,配制好执行脚本后可以使用 npm run watch 进行调用。

"scripts":{
"watch":"webpack --watch", // 此时使用观察者模式
"build":"webpack -p", // 用于给项目进行打包的命令
}

HMR 热模块替换

允许运行时更新各种模块,而无需进行完全刷新。

Hot Module Replacement 开启热模块替换插件,也被称为HMR,永远不要在生产环境下启用HMR。

插件的配置

new webpack.HotModuleReplacementPlugin({
// multiStep(boolean):设置为true时,插件会分成两步构建文件。首先编译加载chunks,之后再编译剩余的通常的资源
// fullBuildTimeout(number):当 multiStep 启用时,表示两步构建之间的延时
// requestTimeout(number):下载 manifest 的延时
// 这些选项属于实验性内容,因此以后可能会被弃用。就如同上文所说的那样,这些选项通常情况下都是没有必要设置的,仅仅是设置一下 new webpack.HotModuleReplacementPlugin() 在大部分情况下就足够了。
})

加载器loader

在实际的开发过程中,webpack 默认只能打包处理 js 文件,不能处理其他文件,处理其他文件时,必须使用 loader ,否则会报错。

  • less-loader 可以打包处理 .less 相关的文件
  • sass-loader 可以打包处理 .scss 相关的文件
  • url-loader 可以打包处理 css 中与 url 路径相关的文件

使用 less 或者 sass

module:{
rules:[
{test:/\.less$/ ,use:['style-loader','css-loader','less-loader']},
// 其中 use 可以接受数组,也可以接受单一一个名称,比如 use:'style-loader'
// css 文件编译次序依次为 less-loader css-loader style-loader
// 在前一个编译完成后,一次传递给下一个,最终输出文件
// {test:/\.scss$/ ,use:['style-loader','css-loader','sass-loader']}
// 使用 sass
]
}
// 或者换一种格式进行书写
module: {
rules: [{
test: /\.css$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
modules: true
}
}]
}]
}

几乎所有的 loader 都需要进行 use 在配置但不需要进行 require 引入

浏览器样式兼容

运行 npm i posscss-loader autoprefixer -D

postCSS :做浏览器兼容

autoprefixer : 自动修改兼容前缀

// 创建 postcss 的配置文件 postcss.config.js 
const autoprefixer = require('autoprefixer')
module.exports ={
plugins:[autoprefixer]
}
// 并且在 webpack 的 css 配置后面额外添加一个 postcss-loader
module.exports={
module:{
rules:[{test:/\.css$/ ,use:['style-loader','css-loader']}]
}
}

图片样式

使用 url 进行背景图插入的时候,会报错,可以使用 url-loaderfile-loader 解决。

其中 file-loaderurl-loader 内的依赖项

照旧,先安装上,npm i url-loader file-loader -D

// webpack.config.js
module.exports={
module:{
rules:[{
test:/\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,
use:'url-loader?limit=16940' // limit 用于限制图片的大小,单位是字节
}]
}
}

JS高级语法

使用最新的语法,可能会报错,所以需要 babel进行语法转化

安装 babel 转换器相关的包 babel-loader babel/core babel/runtime

// babel.config.js
module.exports = {
presets:['@babel/preset-env'],
plugins:['@babel/plugin-transform-runtime','@babel/plugin-proposal-class-properties']
}
// webpack.config.js 中 module 里面的 rules 数组中添加该对象
{test:/\.js$/,use:'babel-loader',exclude:/node_modules/}
// exclude 该方法添加后,不会处理,node_modules 中的 js 文件

plugin

使用

const HtmlWebpackPlugin = require('html-webpack-plugin')

特性:一部分插件需要单独安装,一部分插件在 webpack 中内置了,但是所有的插件都必须通过 require 引入。

分析:webpack插件都是一个具有 apply 属性的 JavaScript 对象,apply属性会被 webpack complier 调用,并且 complier 对象可在整个编译的生命周期进行访问。

const pluginName = 'ConsoleLogOnBuildWebpackPlugin';

class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tap(pluginName, compilation => {
console.log("webpack 构建过程开始!");
});
}
}

HtmlWebpackPlugin

为 html 文件中引入的外部资源如 scriptlink 等资源。每次都会为他们动态添加 compile 后的 hash,防止引用缓存的外部文件问题。

生成项目的主入口 html 文件,一般原则是单应用的话会生成一个 html 文件,多应用的话会生成多个对应的 html 文件。

dist目录生成index.html文件,而且会自动更新,不需要我们手动添加或修改index.html文件。

HtmlWebpackPlugin配置

nametypedefaultdescription
titlestringWebpack App用于生成html文档的标题
filenamestringindex.html要写入html的文件,默认为'index.html',在这里可以指定一个目录(eg:assets/admin.html)
templatestring``webpack需要模板的路径
templateParametersBoolean/Object/Function``允许重写template中引用的参数
templateContentstring/function``一个String/Function包含template内容的字符串。template和templateContent不可同时使用
injectBoolean/Stringtruetrue/head/body/false将所有的assets注入到给定的template或templateContent。当传入true/body时,所有JavaScript资源将放置在body的底部。传入head将把脚本放在head中
faviconString``向html输出facicon路径
metaObject允许注入meta标签
minifyBoolean/Objectfalse将html-minifiler的配置作为对象来缩小输出
hashBooleanfalse如果为true,则将唯一的webpack编译hash追加到所有包含js和css文件中。这对于缓存崩溃是有用的
cacheBooleantrue仅在文件被更改时才发出该文件
showErrorsBooleantrue错误详情将被写到html页面
chunks??允许只添加一些chunks
chunksSortModeString/Functionauto允许控制chunks被包含到html之前应该如何排序。允许值:none/auto/dependency/manual/function
excludeChunksArray``允许跳过一些chunks
xhtmlBooleanfalse如果为true设置link标签为自动关闭

清理 dist 文件夹

由于过去的代码会遗留在 dist 文件夹中,导致 dist 文件夹十分混乱,webpack 无法追踪那些文件是那个项目中用到的,因此推荐安装 clean-webpack-plugin 在每次构建前清理 dist 文件夹。

npm i clean-webpack-plugin -D

const CleanWebpackPlugin = require('clean-webpack-plugin');
// 在 webpack config 中进行引入
{
plugins:[
new CleanWebpackPlugin(paths[,{options}])
]
}
//options and defaults(Options),下面是该插件的一些配置选项,不需要再项目中都配置。
{
// wepack根文件夹的绝对路径,默认:package的根目录
root:__dirname,
// 向console写日志
werbose:true,
//使用'true'来测试或仿真删除(将不会移除文件)
//默认值:'false',移除文件
dry:false,
//如果为'true',则删除文件重新编译
watch:false,
// Instead of removing whole path recursively,
// remove all path's content with exclusion of provided immediate children.
// Good for not removing shared files from build directories.
exclude: [ 'files', 'to', 'ignore' ],
// 允许插件清理webpack目录以外的文件夹
allowExternal:false,
//在文件被发送的output文件夹之前执行清理
beforeEmit:false
}

其它核心概念

模式

选项描述
development会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPluginNamedModulesPlugin
production会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPluginUglifyJsPlugin.

target

webpack 能够为 多种环境 或 target 构建编译(编译后代码 的运行环境)

默认值:web

常见值 https://link.juejin.im?target=https%3A%2F%2Fwww.webpackjs.com%2Fconfiguration%2Ftarget%2F

source map

由于打包时,可能很难追踪到错误和警告在源代码中的原始位置。例如,如果三个源文件打包好后,如果报错,只会指向bundle.js。而无法告诉你确切的错误位置。

source map 会改变代码中显示错误的方式(打包后代码、生成后代码、转换过代码、源代码等。)

会影响构建(build )重新构建的速度

整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它

  • eval-source-map

    构建速度:-- 、重新构建速度:+ 、生产环境:no 、显示原始源代码

  • cheap-eval-source-map

    构建速度:+ 、重新构建速度:++ 、生产环境:no 、转换过的代码(仅限行)

总结:需要注意的是不同的 devtool 的设置,会导致不同的性能差异。

  • "eval" 具有最好的性能,但并不能帮助你转译代码。
  • 如果你能接受稍差一些的 mapping 质量,可以使用 cheap-source-map 选项来提高性能
  • 使用 eval-source-map 配置进行增量编译
  • 在大多数情况下,cheap-module-eval-source-map 是最好的选择

动态引入

const zhCN = import(/* webpackChunkName: "zhcn-lib" */'ant-design-vue/lib/locale-provider/zh_CN')
// 实现引入,在注释处可以标记打包后的文件名称。

参考文章

文章名称链接
官方文档https://www.webpackjs.com/concepts/